package net.citrite.android.translator;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Translation {
    private static AdmAuthentication admAuthentication = new AdmAuthentication();
    private static final String SERVICE_URL = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?";
    private static final String PARAM_TO_LANG = "&to=",
            PARAM_FROM_LANG = "&from=",
            PARAM_TEXT_SINGLE = "&text=";
    private static Map<String, Map<String, String>> Caches = new HashMap<String, Map<String, String>>() {
        {
            put("zh-CHS", new HashMap<String, String>());
            put("zh-CHT", new HashMap<String, String>());
            put("ja", new HashMap<String, String>());
            put("de", new HashMap<String, String>());
            put("bg", new HashMap<String, String>());
            put("es", new HashMap<String, String>());
            put("fr", new HashMap<String, String>());
            put("ko", new HashMap<String, String>());
            put("nl", new HashMap<String, String>());
            put("pt", new HashMap<String, String>());
            put("ru", new HashMap<String, String>());
        }
    };
    private static Map<String, String> LangCodes = new HashMap<String, String>() {
        {
            put("zh-rCN", "zh-CHS");
            put("zh-rTW", "zh-CHT");
        }
    };
    public static String Translate(String orig, String language) throws IOException {
        if (LangCodes.containsKey(language)) {
            language = LangCodes.get(language);
        } else if (language.length() > 2){
            language = language.substring(0, 2);
        }
        Map<String, String> cache = null;
        if (Caches.containsKey(language)) {
            cache = Caches.get(language);
        }
        if (cache != null && cache.containsKey(orig)) {
            return cache.get(orig);
        }
        final String params = PARAM_FROM_LANG + URLEncoder.encode("en", "UTF-8")
                        + PARAM_TO_LANG + URLEncoder.encode(language.toString(), "UTF-8")
                        + PARAM_TEXT_SINGLE + URLEncoder.encode(orig, "UTF-8");

        final URL url = new URL(SERVICE_URL + params);

        String tokenJson = admAuthentication.getToken();
        Integer expiresIn = Integer.parseInt((String) ((JSONObject) JSONValue.parse(tokenJson)).get("expires_in"));
        //long tokenExpiration = System.currentTimeMillis() + ((expiresIn * 1000) - 1);
        String token = "Bearer " + ((JSONObject)JSONValue.parse(tokenJson)).get("access_token");
        final HttpURLConnection uc = (HttpURLConnection) url.openConnection();
        uc.setRequestProperty("Content-Type", "text/plain; charset=UTF-8");
        uc.setRequestProperty("Accept-Charset", "UTF-8");
        uc.setRequestProperty("Authorization", token);
        uc.setRequestMethod("GET");
        uc.setDoOutput(true);
        try {
            final int responseCode = uc.getResponseCode();
            final String result = inputStreamToString(uc.getInputStream());
            if(responseCode!=200) {
                throw new IOException("Error from Microsoft Translator API: " + result);
            }
            String translation =  (String)JSONValue.parse(result);
            if (translation == null || translation.isEmpty()) {
                translation = orig;
            }
            if (cache != null) {
                cache.put(orig, translation);
            }
            return translation;
        } finally {
            if(uc!=null) {
                uc.disconnect();
            }
        }
    }
    /**
     * Reads an InputStream and returns its contents as a String.
     * Also effects rate control.
     * @param inputStream The InputStream to read from.
     * @return The contents of the InputStream as a String.
     * @throws Exception on error.
     */
    private static String inputStreamToString(final InputStream inputStream) throws IOException {
        final StringBuilder outputBuilder = new StringBuilder();

        try {
            String string;
            if (inputStream != null) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
                while (null != (string = reader.readLine())) {
                    // Need to strip the Unicode Zero-width Non-breaking Space. For some reason, the Microsoft AJAX
                    // services prepend this to every response
                    outputBuilder.append(string.replaceAll("\uFEFF", ""));
                }
            }
        } catch (Exception ex) {
            throw new IOException("[microsoft-translator-api] Error reading translation stream.", ex);
        }

        return outputBuilder.toString();
    }

    static class AdmAuthentication {
        private static String DatamarketAccessUri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13";
        private static String CliendId = "FINDLANGLENGTH";
        private static String ClientSec = "PB55FvgqTKhJJSz/r2/M5EPjSGUTbCT3mUzan/AU+G8=";
        private String token = null;
        private String params = null;
        private ReadWriteLock lock = new ReentrantReadWriteLock(false);
        private Timer timer = new Timer(true);

        public AdmAuthentication() {
            try {
                params = "grant_type=client_credentials&scope=http://api.microsofttranslator.com&client_id=" + URLEncoder.encode(CliendId, "UTF-8") + "&client_secret=" + URLEncoder.encode(ClientSec, "UTF-8");
                getToken(params);
            } catch (IOException e) {
                e.printStackTrace();
            }
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        getToken(params);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }, 500000, 500000);

        }

        public String getToken() {
            String t;
            lock.readLock().lock();
            t = token;
            lock.readLock().unlock();
            return t;
        }

        private void getToken(String params) throws IOException {
            URL url = new URL(DatamarketAccessUri);
            HttpURLConnection uc = (HttpURLConnection)url.openConnection();

            uc.setRequestProperty("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
            uc.setRequestProperty("Accept-Charset", "UTF-8");
            uc.setRequestMethod("POST");
            uc.setDoOutput(true);

            OutputStreamWriter wr = new OutputStreamWriter(uc.getOutputStream());
            wr.write(params);
            wr.flush();

            try {
                final int responseCode = uc.getResponseCode();
                final String result = inputStreamToString(uc.getInputStream());
                if(responseCode!=200) {
                    throw new IOException("Error from Microsoft Translator API: " + result);
                }
                lock.writeLock().lock();
                token = result;
                lock.writeLock().unlock();
            } finally {
                if(uc!=null) {
                    uc.disconnect();
                }
            }
        }
    }
}

